package com.simon.cdc.event;


import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;

/**
 * Debezium格式JSON解析器
 */
public final class ChangeEventParser {

    /**
     * 解析给定的JSON格式变更事件字符串
     * 此方法负责将JSON字符串转换为ChangeEvent对象，包括事件的源表名、操作类型及受影响的记录
     *
     * @param jsonEvent JSON格式的变更事件字符串，包含变更事件的详细信息
     * @return 返回一个ChangeEvent对象，封装了变更事件的所有相关信息
     */
    public static ChangeEvent parse(String jsonEvent) {
        // 解析JSON字符串为JSONObject对象，便于后续提取具体信息
        JSONObject json = JSONUtil.parseObj(jsonEvent);

        // 提取事件源表名，用于标识变更发生的表
        String tableName = json.getJSONObject("source").getStr("table");

        // 解析操作类型，根据JSON中的"op"字段确定是插入、更新还是删除操作
        ChangeEvent.OperationType operation = parseOperation(json.getStr("op"));

        // 创建并返回一个新的ChangeEvent对象，包含变更事件的所有相关信息
        return new ChangeEvent(
                tableName,
                operation,
                json.getJSONObject("before"),
                json.getJSONObject("after"),
                extractPrimaryKeys(json)
        );
    }

    /**
     * 解析操作代码以确定变更事件的类型
     * 此方法将字符串操作代码映射到ChangeEvent.OperationType枚举的相应操作类型
     * 支持的操作代码为：
     * - "c": 创建/插入操作
     * - "r": 读取操作
     * - "u": 更新操作
     * - "d": 删除操作
     * 如果操作代码不匹配任何已知类型，将抛出IllegalArgumentException
     *
     * @param opCode 字符串形式的操作代码，表示变更事件的类型
     * @return 对应的ChangeEvent.OperationType枚举值
     * @throws IllegalArgumentException 如果操作代码不受支持
     */
    private static ChangeEvent.OperationType parseOperation(String opCode) {
        return switch (opCode) {
            case "c" -> ChangeEvent.OperationType.INSERT;
            case "r" -> ChangeEvent.OperationType.READ;
            case "u" -> ChangeEvent.OperationType.UPDATE;
            case "d" -> ChangeEvent.OperationType.DELETE;
            default -> throw new IllegalArgumentException("Unsupported operation code: " + opCode);
        };
    }

    /**
     * 从JSON对象中提取主键信息
     * <p>
     * 此方法假设JSON对象包含一个名为"pk"的键，其值为另一个JSON对象，
     * 该对象代表了数据记录的主键信息这种方法简化了实际的提取逻辑，
     * 在真实应用场景中，可能需要根据更复杂的规则或条件来提取主键信息
     *
     * @param json 包含主键信息的JSON对象
     * @return 包含主键数据的JSON对象如果指定键不存在或值不是JSON对象，则抛出异常
     */
    private static JSONObject extractPrimaryKeys(JSONObject json) {
        // 实际实现应根据CDC事件格式提取主键
        return json.getJSONObject("pk");
    }
}